package io.noties.markwon.plugin.uml.model


class UmlClass : UmlType {
    enum class UmlClassType {
        JAVA_CLASS, ABSTRACT_CLASS, INTERFACE, ENUM
    }

    //    **********************************************************************************************
    //    Getters and setters
    //    **********************************************************************************************
    var umlClassType = UmlClassType.JAVA_CLASS
    var attributes: ArrayList<UmlClassAttribute>
    var umlClassAttributeCount: Int
    var methods: ArrayList<UmlClassMethod>
    var umlClassMethodCount: Int
    var values: ArrayList<UmlEnumValue>
    var valueCount: Int
    var classOrder = 0
    var umlClassNormalXPos = 0f
    var umlClassNormalYPos = 0f
    var umlClassNormalWidth = 0f
    var umlClassNormalHeight = 0f

    //    **********************************************************************************************
    //    Constructors
    //    **********************************************************************************************
    constructor(classOrder: Int) {
        attributes = ArrayList()
        methods = ArrayList()
        values = ArrayList()
        umlClassAttributeCount = 0
        umlClassMethodCount = 0
        valueCount = 0
        this.classOrder = classOrder
    }

    @JvmOverloads
    constructor(name: String, umlClassType: UmlClassType = UmlClassType.JAVA_CLASS) : super(
        name,
        TypeLevel.PROJECT
    ) {
        this.umlClassType = umlClassType
        attributes = ArrayList()
        umlClassAttributeCount = 0
        methods = ArrayList()
        umlClassMethodCount = 0
        values = ArrayList()
        valueCount = 0
    }

    constructor(
        name: String, classOrder: Int, umlClassType: UmlClassType,
        attributes: ArrayList<UmlClassAttribute>, attributeCount: Int,
        methods: ArrayList<UmlClassMethod>, methodCount: Int,
        values: ArrayList<UmlEnumValue>, valueCount: Int,
        umlClassNormalXPos: Float, umlClassNormalYPos: Float
    ) : super(name, TypeLevel.PROJECT) {
        this.classOrder = classOrder
        this.umlClassType = umlClassType
        this.attributes = attributes
        umlClassAttributeCount = attributeCount
        this.methods = methods
        umlClassMethodCount = methodCount
        this.values = values
        this.valueCount = valueCount
        this.umlClassNormalXPos = umlClassNormalXPos
        this.umlClassNormalYPos = umlClassNormalYPos
    }

    constructor(
        name: String, umlClassType: UmlClassType,
        attributes: ArrayList<UmlClassAttribute>,
        methods: ArrayList<UmlClassMethod>,
        values: ArrayList<UmlEnumValue>,
        umlClassNormalXPos: Float, umlClassNormalYPos: Float
    ) : super(name, TypeLevel.PROJECT) {
        this.umlClassType = umlClassType
        this.attributes = attributes
        umlClassAttributeCount = 0
        this.methods = methods
        umlClassMethodCount = 0
        this.values = values
        valueCount = 0
        this.umlClassNormalXPos = umlClassNormalXPos
        this.umlClassNormalYPos = umlClassNormalYPos
    }

    val normalRightEnd: Float
        get() = umlClassNormalXPos + umlClassNormalWidth
    val normalBottomEnd: Float
        get() = umlClassNormalYPos + umlClassNormalHeight


    fun findAttributeByOrder(attributeOrder: Int): UmlClassAttribute? {
        for (a in attributes) if (a.attributeOrder == attributeOrder) return a
        return null
    }

    fun findMethodByOrder(methodOrder: Int): UmlClassMethod? {
        for (m in methods) if (m.methodOrder == methodOrder) return m
        return null
    }

    fun findValueByOrder(valueOrder: Int): UmlEnumValue? {
        for (v in values) if (v.valueOrder == valueOrder) return v
        return null
    }

    fun getAttribute(attributeName: String): UmlClassAttribute? {
        for (a in attributes) if (a.name == attributeName) return a
        return null
    }

    //    **********************************************************************************************
    //    Modifiers
    //    **********************************************************************************************
    fun addMethod(method: UmlClassMethod) {
        methods.add(method)
        umlClassMethodCount++
    }

    fun removeMethod(method: UmlClassMethod) {
        methods.remove(method)
    }

    fun addAttribute(attribute: UmlClassAttribute) {
        attributes.add(attribute)
        umlClassAttributeCount++
    }

    fun removeAttribute(attribute: UmlClassAttribute) {
        attributes.remove(attribute)
    }

    fun addValue(value: UmlEnumValue) {
        values.add(value)
        valueCount++
    }

    fun removeValue(value: UmlEnumValue) {
        values.remove(value)
    }

    fun incrementUmlClassAttributeCount() {
        umlClassAttributeCount++
    }

    fun incrementUmlClassMethodCount() {
        umlClassMethodCount++
    }

    fun incrementValueCount() {
        valueCount++
    }

    //    **********************************************************************************************
    //    Test methods
    //    **********************************************************************************************
    fun containsPoint(absoluteX: Float, absoluteY: Float): Boolean {
        return absoluteX <= umlClassNormalXPos + umlClassNormalWidth && (absoluteX >= umlClassNormalXPos && absoluteY <= umlClassNormalYPos + umlClassNormalHeight) && absoluteY >= umlClassNormalYPos
    }

    fun isSouthOf(umlClass: UmlClass): Boolean {
        //is this in South quarter of umlClass ?
        return umlClassNormalYPos >= umlClass.normalBottomEnd && (normalRightEnd >= umlClass.umlClassNormalXPos - umlClassNormalYPos + umlClass.normalBottomEnd) && umlClassNormalXPos <= umlClass.normalRightEnd + umlClassNormalYPos - umlClass.normalBottomEnd
    }

    fun isNorthOf(umlClass: UmlClass): Boolean {
        //is this in North quarter of umlClass ?
        return normalBottomEnd <= umlClass.umlClassNormalYPos && (normalRightEnd >= umlClass.umlClassNormalXPos - umlClass.umlClassNormalYPos + normalBottomEnd) && umlClassNormalXPos <= umlClass.normalRightEnd + umlClass.umlClassNormalYPos - normalBottomEnd
    }

    fun isWestOf(umlClass: UmlClass): Boolean {
        //is this in West quarter of umlClass ?
        return normalRightEnd <= umlClass.umlClassNormalXPos && (normalBottomEnd >= umlClass.umlClassNormalYPos - umlClass.umlClassNormalXPos + normalRightEnd) && umlClassNormalYPos <= umlClass.normalBottomEnd + umlClass.umlClassNormalXPos - normalRightEnd
    }

    fun isEastOf(umlClass: UmlClass): Boolean {
        //is this in East Quarter of umlClass ?
        return umlClassNormalXPos >= umlClass.normalRightEnd && (normalBottomEnd >= umlClass.umlClassNormalYPos - umlClassNormalXPos + umlClass.normalRightEnd) && umlClassNormalYPos <= umlClass.normalBottomEnd + umlClassNormalXPos - umlClass.normalRightEnd
    }

    fun isInvolvedInRelation(umlRelation: UmlRelation): Boolean {
        return this === umlRelation.relationOriginClass || this === umlRelation.relationEndClass
    }

    fun alreadyExists(inProject: UmlProject): Boolean {
        //check whether class name already exists
        for (c in inProject.umlClasses) if (name == c.name) return true
        return false
    }

    fun containsAttributeNamed(attributeName: String): Boolean {
        for (a in attributes) if (a.name == attributeName) return true
        return false
    }

    fun containsEquivalentMethodTo(method: UmlClassMethod?): Boolean {
        for (m in methods) if (m.isEquivalentTo(method!!)) return true
        return false
    }



    companion object {
        private const val JSON_CLASS_NAME = "ClassName"
        private const val JSON_CLASS_INDEX = "ClassIndex"
        private const val JSON_CLASS_CLASS_TYPE = "ClassClassType"
        private const val JSON_CLASS_ATTRIBUTES = "ClassAttributes"
        private const val JSON_CLASS_METHODS = "ClassMethods"
        private const val JSON_CLASS_VALUES = "ClassValues"
        private const val JSON_CLASS_NORMAL_XPOS = "ClassNormalXPos"
        private const val JSON_CLASS_NORMAL_YPOS = "ClassNormalYPos"
        private const val JSON_CLASS_ATTRIBUTE_COUNT = "ClassAttributeCount"
        private const val JSON_CLASS_METHOD_COUNT = "ClassMethodCount"
        private const val JSON_CLASS_VALUE_COUNT = "ClassValueCount"

    }
}
